/**
 * @file JwtUtils.cpp
 * @brief JWT工具类实现
 * @author CloudLinker
 * @date 2023-06-14
 */

#include "CLink/Utils/JwtUtils.h"
#include <Poco/JWT/Token.h>
#include <Poco/JWT/Signer.h>
#include <Poco/JWT/JWTException.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/JSON/Parser.h>
#include <Poco/JSON/Object.h>
#include <Poco/Exception.h>
#include <Poco/Timestamp.h>
#include <Poco/DateTimeFormatter.h>
#include <Poco/DateTimeFormat.h>
#include <algorithm>
#include <stdexcept>

namespace CLink
{
namespace Utils
{

JwtUtils::JwtUtils(const std::string &secret, const std::string &issuer, int expiresIn)
    : secret_(secret), issuer_(issuer), expiresIn_(expiresIn)
{
    // 初始化默认验证选项
    defaultVerificationOptions_.checkExpiration = true;
    defaultVerificationOptions_.checkIssuer = true;
    defaultVerificationOptions_.checkNotBefore = true;
    defaultVerificationOptions_.allowedAlgorithms = {"HS256"};
}

std::string JwtUtils::generateToken(
    const std::string &subject,
    const std::map<std::string, std::string> &claims,
    const GenerationOptions &options)
{

    try
    {
        // 创建JWT令牌
        Poco::JWT::Token token;

        // 设置标准声明
        token.setSubject(subject);
        token.setIssuer(issuer_);

        // 设置当前时间为签发时间
        Poco::Timestamp now;
        token.setIssuedAt(now);

        // 设置过期时间
        Poco::Timestamp expiration(now);
        expiration += expiresIn_ * Poco::Timestamp::resolution(); // 转换为微秒
        token.setExpiration(expiration);

        // 设置生效时间（如果提供）
        if (options.notBefore.has_value())
        {
            Poco::Timestamp notBefore;
            notBefore = Poco::Timestamp::fromEpochTime(options.notBefore.value());
            token.setNotBefore(notBefore);
        }

        // 设置令牌ID（如果提供）
        if (options.jwtId.has_value())
        {
            token.setId(options.jwtId.value());
        }

        // 添加自定义声明
        for (const auto &claim : claims)
        {
            token.payload().set(claim.first, claim.second);
        }

        // 添加自定义头部
        for (const auto &header : options.headers)
        {
            token.header().set(header.first, header.second);
        }

        // 创建签名者并签名令牌
        Poco::JWT::Signer signer(options.algorithm);
        return signer.sign(token, secret_);
    }
    catch (const Poco::Exception &ex)
    {
        throw std::runtime_error("JWT token generation failed: " + std::string(ex.what()));
    }
}

bool JwtUtils::verifyToken(const std::string &token, const VerificationOptions &options)
{
    try
    {
        if (isEmptyToken(token))
        {
            return false;
        }

        // 检查令牌是否在黑名单中
        if (isTokenBlacklisted(token))
        {
            return false;
        }

        // 解析令牌
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return false;
        }

        // 获取算法
        std::string alg = getAlgorithm(token);
        if (alg.empty())
        {
            return false;
        }

        // 检查算法是否允许
        if (!options.allowedAlgorithms.empty())
        {
            bool algorithmAllowed = false;
            for (const auto &allowedAlg : options.allowedAlgorithms)
            {
                if (alg == allowedAlg)
                {
                    algorithmAllowed = true;
                    break;
                }
            }
            if (!algorithmAllowed)
            {
                return false;
            }
        }

        // 验证签名
        if (!verifySignature(token, alg))
        {
            return false;
        }

        // 检查过期时间
        if (options.checkExpiration && isTokenExpired(token))
        {
            return false;
        }

        // 检查发行者
        if (options.checkIssuer)
        {
            std::string tokenIssuer = jwtToken.getIssuer();
            if (tokenIssuer != issuer_)
            {
                return false;
            }
        }

        // 检查生效时间
        if (options.checkNotBefore)
        {
            Poco::Timestamp notBefore = getNotBefore(token);
            if (notBefore != 0)
            {
                Poco::Timestamp now;
                if (now < notBefore)
                {
                    return false;
                }
            }
        }

        return true;
    }
    catch (const Poco::Exception &)
    {
        return false;
    }
}

std::string JwtUtils::getClaim(const std::string &token, const std::string &claimName)
{
    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return "";
        }

        if (jwtToken.payload().has(claimName))
        {
            return jwtToken.payload().getValue<std::string>(claimName);
        }

        return "";
    }
    catch (const Poco::Exception &)
    {
        return "";
    }
}

std::map<std::string, std::string> JwtUtils::getAllClaims(const std::string &token)
{
    std::map<std::string, std::string> claims;

    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return claims;
        }

        const Poco::JSON::Object &payload = jwtToken.payload();
        for (const auto &name : payload.getNames())
        {
            try
            {
                claims[name] = payload.getValue<std::string>(name);
            }
            catch (const Poco::Exception &)
            {
                // 如果值不是字符串，则跳过
                continue;
            }
        }

        return claims;
    }
    catch (const Poco::Exception &)
    {
        return claims;
    }
}

bool JwtUtils::isTokenExpired(const std::string &token)
{
    try
    {
        Poco::Timestamp expTime = getExpirationTime(token);
        if (expTime == 0)
        {
            return true; // 如果没有过期时间，视为已过期
        }

        Poco::Timestamp now;
        return now >= expTime;
    }
    catch (const Poco::Exception &)
    {
        return true;
    }
}

Poco::Timestamp JwtUtils::getExpirationTime(const std::string &token)
{
    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return 0;
        }

        return jwtToken.getExpiration();
    }
    catch (const Poco::Exception &)
    {
        return 0;
    }
}

std::string JwtUtils::refreshToken(
    const std::string &token,
    const std::map<std::string, std::string> &additionalClaims)
{

    try
    {
        if (!verifyToken(token))
        {
            throw std::runtime_error("Cannot refresh invalid token");
        }

        // 解析原始令牌
        Poco::JWT::Token jwtToken = parseToken(token);

        // 获取原始声明
        std::map<std::string, std::string> claims = getAllClaims(token);

        // 添加或更新额外声明
        for (const auto &claim : additionalClaims)
        {
            claims[claim.first] = claim.second;
        }

        // 获取主题
        std::string subject = jwtToken.getSubject();

        // 创建生成选项
        GenerationOptions options;
        options.algorithm = getAlgorithm(token);

        // 获取令牌ID
        std::string tokenId = getTokenId(token);
        if (!tokenId.empty())
        {
            options.jwtId = tokenId;
        }

        // 将原始令牌加入黑名单
        blacklistToken(token, "Refreshed");

        // 生成新令牌
        return generateToken(subject, claims, options);
    }
    catch (const Poco::Exception &ex)
    {
        throw std::runtime_error("Token refresh failed: " + std::string(ex.what()));
    }
}

bool JwtUtils::blacklistToken(const std::string &token, const std::string &reason)
{
    try
    {
        if (isEmptyToken(token))
        {
            return false;
        }

        // 清理过期的黑名单条目
        cleanupBlacklist();

        // 将令牌添加到黑名单（忽略reason参数）
        (void)reason; // 显式忽略未使用的参数
        blacklist_.insert(token);
        return true;
    }
    catch (const std::exception &)
    {
        return false;
    }
}

bool JwtUtils::isTokenBlacklisted(const std::string &token)
{
    return blacklist_.find(token) != blacklist_.end();
}

bool JwtUtils::removeFromBlacklist(const std::string &token)
{
    auto it = blacklist_.find(token);
    if (it != blacklist_.end())
    {
        blacklist_.erase(it);
        return true;
    }
    return false;
}

Poco::Timestamp JwtUtils::getIssuedAt(const std::string &token)
{
    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return 0;
        }

        return jwtToken.getIssuedAt();
    }
    catch (const Poco::Exception &)
    {
        return 0;
    }
}

Poco::Timestamp JwtUtils::getNotBefore(const std::string &token)
{
    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return 0;
        }

        return jwtToken.getNotBefore();
    }
    catch (const Poco::Exception &)
    {
        return 0;
    }
}

std::string JwtUtils::getTokenId(const std::string &token)
{
    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return "";
        }

        return jwtToken.getId();
    }
    catch (const Poco::Exception &)
    {
        return "";
    }
}

std::string JwtUtils::getAlgorithm(const std::string &token)
{
    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        if (jwtToken.payload().size() == 0)
        {
            return "";
        }

        return jwtToken.getAlgorithm();
    }
    catch (const Poco::Exception &)
    {
        return "";
    }
}

bool JwtUtils::isEmptyToken(const std::string &token)
{
    if (token.empty())
    {
        return true;
    }

    try
    {
        Poco::JWT::Token jwtToken = parseToken(token);
        return jwtToken.payload().size() == 0;
    }
    catch (const Poco::Exception &)
    {
        return true;
    }
}

void JwtUtils::setDefaultExpiresIn(int expiresIn)
{
    if (expiresIn > 0)
    {
        expiresIn_ = expiresIn;
    }
}

void JwtUtils::setDefaultVerificationOptions(const VerificationOptions &options)
{
    defaultVerificationOptions_ = options;
}

Poco::JWT::Token JwtUtils::parseToken(const std::string &token)
{
    try
    {
        return Poco::JWT::Token(token);
    }
    catch (const Poco::Exception &)
    {
        return Poco::JWT::Token();
    }
}

bool JwtUtils::verifySignature(const std::string &token, const std::string &algorithm)
{
    try
    {
        // 创建签名者，只传入算法
        Poco::JWT::Signer signer(algorithm);
        // verify方法接受字符串并返回Token对象
        // 如果验证失败会抛出异常，所以如果能返回Token对象就说明验证成功
        signer.verify(token);
        return true;
    }
    catch (const Poco::Exception &)
    {
        return false;
    }
}

void JwtUtils::cleanupBlacklist()
{
    // 这里可以实现黑名单清理逻辑
    // 例如，移除已过期的令牌
    std::vector<std::string> tokensToRemove;

    for (const auto &token : blacklist_)
    {
        if (isTokenExpired(token))
        {
            tokensToRemove.push_back(token);
        }
    }

    for (const auto &token : tokensToRemove)
    {
        blacklist_.erase(token);
    }
}

} // namespace Utils
} // namespace CLink